/**
 * 定时器0
 * STP-ISP --> 右侧选择"定时器计算器" --> 选择:
 * 频率      : 11.0592MHz   (误差0.04%)
 * 定时长度  : 1毫秒
 * 选择定时器: 定时器0
 * 定时器模式: 16位不自动重载 (89C52没有16位重载. 比16位自动重载, 多设置1行代码: TMOD |= 0x01;)
 * 定时器时钟: 12T(FOSC/12)     --> 复制代码 (12T: 每 12 个时钟加 1 )
 * 
 * TMOD: 定时器/计数器 工作模式寄存器, 设置 定时器0:
 *       GATE  C/T  M1  M0    GATE  C/T  M1  M0
 *       \   定时器1    /     \   定时器0    /
 *       GATE = 0
 *       C/T=0(定时器模式)
 *       M1=0, M0=0: 模式0, 13位定时器/计数器， 兼容8048定时模式，TL只用低5位参与分频，TH整个8位全用 。
 *       M1=0, M0=1: 模式1, 表示 16位定时器/计数器，TL、TH全用
 *       M1=1, M0=0: 模式2, 8位自动重装载定时器, 当溢出时将TH存放的值自动重装入TL 。
 *       M1=1, M0=1: 模式3, 定时器0此时作为双8位定时器/计数器。TL0作为一个8位定时器/计数器，通过标准定"时器0的控制位"控制。
 *                                                           TH0仅作为一个8位定时器, 由"定时器1的控制位"控制。
 *                          定时器/计数器1此时无效（停止计数）。
 * 
 * TH0 & TL0: 定时器/计数器0的 相关寄存器, 初值计算, 见下方{@link void Timer0_set_TH_TL_Value(unsigned int)} 方法
 * 
 * TF0 & TR0: 定时器/计数器 中断控制寄存器(TCON: 可位寻址)
 */
#include <REGX52.H>
#include "Delay_Utils.h"

//震荡频率=11.0592MHz       //12MHz:12000000
// static code const unsigned long int frequency = 11059200;
#define frequency 11059200
/**
 * 模式1(16位定时器/计数器) 计数1次时间 = 1s / 频率f * 12T = 1us/次
 * 1000000.0F: 1s = 10^6us, 并转换成float
 */
static code const float TIMER0_PER_COUNT_TIME = 1000000.0F / frequency * 12;
//溢出后的重装值
static unsigned int Timer0_TH_Value, Timer0_TL_Value;

unsigned int Timer0_Caculate_TH_TL_by_Time(unsigned int Count_us);
void Timer0_set_TH_TL_Value(unsigned int value);

/**
 * 初始化
 * @param us 定时器模式时, 设置多少时间后溢出, 单位微秒us, 0~65535
 * @param isCounterMode 是否是计数器模式
 */
void Timer0_Init(unsigned int us, bit isCounterMode) {
    // AUXR &= 0x7F;        //定时器时钟12T模式     (89C52没有 AUXR 寄存器, 89C52已经是12T模式) (12T: 每 12 个时钟加 1 )
    TMOD &= 0xF0;           //设置定时器模式        (定时器1 高4位不变)
    TMOD |= 0x01;           //设置定时器模式        (定时器0 低4位0001)

    //如果是计数器模式
    if (isCounterMode) {
        Timer0_set_TH_TL_Value(0);
    } else {
        //设置定时初始值
        us = Timer0_Caculate_TH_TL_by_Time(us);
        Timer0_set_TH_TL_Value(us);
    }

    TF0 = 0;                //清除TF0标志           (timer flag)
    //定时器0开始计时       (timer run).  计数器不自动开始计数
    TR0 = !isCounterMode;



    /**
     * 初始化 定时器0 中断
     */
    if (!isCounterMode) {
        //enable T0 interrupt, T0的溢出中断允许位。ET0=1，允许T0中断；ET0=0禁止T0中断 (来自"STC89c52.pdf" p157)
        ET0 = 1;
        //enable all, 允许总中断, Global Enable(所有中断的总开关)
        EA = 1;

        /**
         * 中断优先级控制寄存器IP/XICON和IPH (来自"STC89c52.pdf" p162)
         * IPH: 中断优先级控制寄存器高(不可位寻址)
         * XICON : 辅助中断控制寄存器 (可位寻址)
         * IP : 中断优先级控制寄存器低 (可位寻址)
         * 
         * PT0 属于 IP, 可不设, 0: 看图有说明
         */
        PT0 = 0;
    }
}

/**
 * 设置多少时间后发生溢出, 然后算出初值.
 * ★★★注意★★★:
 *  不要在中断函数中计算, 因为计算会占用时间, 导致一些很奇怪的后果!!!
 *      定时器/计数器0的 相关寄存器, 初值计算:
 *            模式1(16位定时器/计数器) 计数1次时间 = 1s / 12MHz * 12T = 1us/次,  16位溢出 = 2^16 = 65536次(0~65535)
 *            ==> 1ms:   1000us / 1us/次   =   计数1000次
 *            ==> 初值: 65536 - 1000 = 64536
 *            ==> 定时器/计数器0和1的 相关寄存器:
 *            ==> TH0 = 64536 / 256;  //高位=252.09375
 *            ==> TL0 = 64536 % 256;  //低位=24
 * @param Count_us 定时多少时间后发生溢出, 0~65535, 单位us
 * @return Timer0 填充初值
 */
unsigned int Timer0_Caculate_TH_TL_by_Time(unsigned int Count_us) {
    //一共计数多少次
    float count = Count_us / TIMER0_PER_COUNT_TIME;
    //定时初始值
    unsigned int value = 65536 - count;
    return value;
}

/**
 * 设置1秒钟的震荡频率, 然后算出初值.
 * @param Frequency 震荡频率, 单位Hz
 * @return Timer0 填充初值
 */
unsigned int Timer0_Caculate_TH_TL_by_Frequency(unsigned long Frequency) {
    /**
     * 机器周期 = 震荡频率 / 12T
     * 最大频率 = 机器周期 / 2      ∵高电平+低电平 = 1个周期, ∴最大频率=机器周期/2
     */
    if (Frequency >= frequency / 12 / 2) return 0;
    //周期 = 1s/频率, 但要/2,  ∵高电平+低电平 = 1个周期
    return Timer0_Caculate_TH_TL_by_Time(1000000.0F / Frequency / 2);
}

/**
 * 重新赋初值, 防止溢出后从0重新开始计数
 */
void Timer0_reset_TH_TL() {
    //12MHz(1ms)
    // TL0 = 0x18;          //设置定时初始值        (0001 1000 = 24)
    // TH0 = 0xFC;          //设置定时初始值        (1111 1100 = 252)
    //11.0592MHz(1ms)   (误差0.04%)
    // TL0 = 0x66;          //设置定时初始值        (0110 0110 = 102)
    // TH0 = 0xFC;          //设置定时初始值        (1111 1100 = 252)
    TL0 = Timer0_TL_Value;
    TH0 = Timer0_TH_Value;
}

/**
 * 给 Timer0 设置初值&重新赋 "初值"
 * @param value 溢出重装载值, 0~65535
 */
void Timer0_set_TH_TL_Value(unsigned int value) {
    Timer0_TL_Value = value % 256;
    Timer0_TH_Value = value / 256;
    Timer0_reset_TH_TL();
}

/**
 * 一般用于计数器模式
 * @return 返回计数多少次
 */
unsigned int Timer0_getCounter() {
    return TH0 << 8 | TL0;
}

/**
 * 一般用于计数器模式
 * @return 返回计数多少时间, 单位us
 */
float Timer0_getCounter_Time() {
    return Timer0_getCounter() * TIMER0_PER_COUNT_TIME;
}

static bit (*Timer0_onTickListener)();
/**
 * 设置 Timer0 的计时监听, 1ms 计时回调1次. (会先调用函数, 然后再根据返回值判断是否重装载TH,TL)
 * 工程中写多个 interrupt 1 会报错, 所以写成监听模式
 * TODO 写成 addListener 模式
 * @param autoResetTH_TL 是否对 TH0, TL0 自动重装载 (周期1ms)
 * @param Timer0_onTick 回调的函数指针, 示例: <br />
 *        bit Timer0_onTick() {
 *            static unsigned int T0Count = 0;
 *            T0Count ++;
 *            if (T0Count >= 500) {   //每500ms进入一次
 *                T0Count = 0;
 *                //do something...
 *            }
 *            return 1;               //是否对 TH0, TL0 自动清0 (周期1ms)
 *        }
 *        //传入函数指针
 *        Timer0_setOnTickListener(&Timer0_onTick);
 */
void Timer0_setOnTickListener(bit (*Timer0_onTick)()) {
    Timer0_onTickListener = Timer0_onTick;
}

static bit Timer0_autoResetTH_TL;
void (*Timer0_onTickListener_Exact)();
/**
 * 设置 Timer0 的计时监听. (如果参1传1, 会先重装载TH,TL, 然后再调用函数, 计时更精确)
 * 工程中写多个 interrupt 1 会报错, 所以写成监听模式
 * TODO 写成 addListener 模式
 * @param autoResetTH_TL 是否对 TH0, TL0 自动重装载
 * @param Timer0_onTick 回调的函数指针, 示例: <br />
 *        static void Timer0_onTick() {
 *            static unsigned char T0Count = 0;
 *            T0Count ++;
 *            if (T0Count >= 10) {   //每隔一段时间进入一次
 *                T0Count = 0;
 *                //do something...
 *            }
 *        }
 *        //传入函数指针
 *        Timer0_setOnTickListener(1|0, &Timer0_onTick);
 */
void Timer0_setOnTickListener_Exact(bit autoResetTH_TL, void (*Timer0_onTick)()) {
    Timer0_autoResetTH_TL = autoResetTH_TL;
    Timer0_onTickListener_Exact = Timer0_onTick;
}

/**
 * Timer0 开始
*/
void Timer0_start() {
    TR0 = 1;
}

/**
 * Timer0 停止
*/
void Timer0_stop() {
    TR0 = 0;
}

/**
 * 停顿一段时间不计时
 * @param ms 暂停多少时间, 单位ms
 */
void Timer0_pause(unsigned char ms) {
    Timer0_stop();
    Delayms(ms);
    Timer0_start();
}

/**
 * interrupt 1: 中断号=1
 */
static void Timer0_Rountine() interrupt 1 {
    //调用函数, 判空
    if (Timer0_onTickListener) {
        bit autoReset = Timer0_onTickListener();
        if (autoReset) Timer0_reset_TH_TL();
    } else if (Timer0_onTickListener_Exact) {
        //自动重置
        if (Timer0_autoResetTH_TL) Timer0_reset_TH_TL();
        Timer0_onTickListener_Exact();
    }
}
